home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 138 / 138.xpi / chrome / stumbleupon.jar / content / stumbleReporter.js < prev    next >
Text File  |  2009-08-17  |  13KB  |  440 lines

  1. //
  2. // su_StumbleReporter
  3. //
  4. // This class encapsulates all of the stumble reporting logic, including storage of stumbled urls and report
  5. // submission retry logic.
  6. //
  7. function su_StumbleReporter(parent)
  8. {
  9.     this._parent = parent;
  10.     this._ds = this._parent.getDatastore();
  11.     this._updateEnableDb();
  12.     this._timer = null;
  13.     this._reportContext = null;
  14.     this._lastReportTimestamp = 0;
  15.     this._retryCount = 0;
  16. }
  17.  
  18. su_StumbleReporter.prototype =
  19. {
  20.     FAIL_NO_SEENCONF: -1,
  21.     FAIL_NORMAL_TIMEOUT: -2,
  22.     FAIL_BACKUP_TIMEOUT: -3,
  23.     
  24.     start: function()
  25.     {
  26.         if (this._timer)
  27.             return;
  28.         
  29.         var reportInterval = this._ds.getValue("@stumble_report_interval_ms");
  30.         var me = this;
  31.         this._timer = this._parent._setInterval( function() { me._checkSendReport(); }, reportInterval );
  32.  
  33.         this._lastReportTimestamp = this._ds.getIntValue("$stumble_last_report");
  34.         this._retryCount = this._ds.getValue("$stumble_report_retrycount");
  35.     },
  36.     
  37.     stop: function()
  38.     {
  39.         if (! this._timer)
  40.             return;
  41.         
  42.         if (this._reportContext)
  43.             this._parent.abortPostAsync(this._reportContext._request);
  44.         
  45.         this._parent._clearInterval(this._timer);
  46.         this._timer = null;
  47.         this._lastReportTimestamp = 0;
  48.         this._retryCount = 0;
  49.     },
  50.     
  51.     addVisitedUrl: function(url)
  52.     {
  53.         if (!url.publicid)
  54.             return;
  55.  
  56.         // First, see if we have to remove one.
  57.  
  58.         if (this._enabledb)
  59.         {
  60.             var db = this._ds.getDatabase();
  61.             
  62.             // prevent the queue from growing without bound
  63.             db.a("select * from stumble_visited_urls order by stumbletime asc");
  64.             var result = this._failsafeQuery(db);
  65.             if (result.length >= this._ds.getValue("@stumble_max_visited_urls"))
  66.                 this.deleteVisitedUrl(result[0]);
  67.                     
  68.             // Insert this visited url into the database.
  69.             db = this._ds.getDatabase();
  70.             db.a("INSERT INTO stumble_visited_urls (publicid, stumbletime, referralid, retrycount) VALUES (");
  71.             db.as(url.publicid);
  72.             db.av(this._parent.callWindow("su_get_time_s"));
  73.             var referralId = '';
  74.             if (url.referralid)
  75.                 referralId = url.referralid;
  76.             db.as(referralId);
  77.             db.alv(this._retryCount);
  78.             this._failsafeQuery(db);
  79.         }
  80.         else
  81.         {
  82.             // prevent the queue from growing without bound
  83.             var result = this._ds.selectAllRows("stumble_visited_urls");
  84.             if (result.length >= this._ds.getValue("@stumble_max_visited_urls"))
  85.             {
  86.                 result.sort(function (a, b)
  87.                         { return parseInt(a.stumbletime) > parseInt(b.stumbletime); });
  88.                 
  89.                 this.deleteVisitedUrl(result[0]);
  90.             }
  91.             
  92.             
  93.             // Insert this visited url into the database.
  94.             var row = new Object();
  95.             row.publicid = url.publicid;
  96.             row.stumbletime = this._parent.callWindow("su_get_time_s");
  97.             row.referralid = (url.referralid) ? url.referralid : '';
  98.             row.retrycount = this._retryCount;
  99.             
  100.             if (! this._ds.selectRow("stumble_visited_urls", "publicid", row.publicid, "referralid", row.referralid))
  101.                 this._ds.insertRow("stumble_visited_urls", row);
  102.             
  103.             this._ds.flushPrefs();
  104.         }
  105.         
  106.         // And perform an active report submission.
  107.         this._reportStumbles(true);
  108.     },
  109.     
  110.     deleteVisitedUrl: function(url)
  111.     {
  112.         if (this._enabledb)
  113.         {
  114.             var db = this._ds.getDatabase();
  115.             db.a("delete from stumble_visited_urls where publicid = " + db.q(url.publicid) + " and referralid=" + db.q(url.referralid));
  116.             this._failsafeQuery(db);
  117.         }
  118.         else
  119.         {
  120.             var row = this._ds.selectRow("stumble_visited_urls", "publicid", url.publicid, "referralid", url.referralid);
  121.             if (row)
  122.                 this._ds.deleteRow(row);
  123.             var referralid = (url.referralid) ? url.referralid : '';
  124.         }
  125.     },
  126.     
  127.     _isReportInProgress: function()
  128.     {
  129.         return (this._ds.getIntValue("$stumble_report_started") != 0)
  130.     },
  131.     
  132.     _checkSendReport: function()
  133.     {
  134.         // First see if there is anything to report.
  135.         var url = this._getNextVisitedUrl();
  136.         if (url == null)
  137.             return;
  138.         
  139.         // See if a report is already in progress.
  140.         if (this._isReportInProgress())
  141.         {
  142.             // Check whether it timed out.
  143.             var submissionTimeout = this._ds.getValue("@stumble_submission_timeout_ms");
  144.             var finishTime = submissionTimeout + this._ds.getIntValue("$stumble_report_started");
  145.             var now = (new Date()).getTime();
  146.             // Note:  We check for timeout, but we also check _reportContext because it is reset when the browser restarts.
  147.             // So if by chance the user gets a stumble_report_started in the future, then we can still detect a backup timeout
  148.             // when their browser is restarted because _reportContext will be reset to NULL in that case.
  149.             if (!this._reportContext || (finishTime <= now))
  150.             {
  151.                 // This report timed out, abort the request.
  152.                 this._onReportDone({ aborted: true, failureCode: this.FAIL_BACKUP_TIMEOUT });
  153.                 return;
  154.             }
  155.             else
  156.             {
  157.                 // It didn't time out yet, nothing to do right now.
  158.                 return;
  159.             }
  160.         }
  161.  
  162.         // Check whether we are in RTO (Retransmission TimeOut) mode.
  163.         if (this._retryCount != 0)
  164.         {
  165.             // Get the timeout value for the current state.
  166.             var timeout = this._getCurrentRTOTimeout();
  167.             var now = (new Date()).getTime();
  168.             if (now > (this._lastReportTimestamp + timeout))
  169.             {
  170.                 // We timed out, bump / store the state, and try reporting stumbles again.
  171.                 this._retryCount++;
  172.                 this._ds.setValue("$stumble_report_retrycount", this._retryCount);
  173.                 this._ds.flushPrefs();
  174.                 this._reportStumbles(false);
  175.             }
  176.         }
  177.         else
  178.         {
  179.             // Nobody is actively reporting stumbles, and we aren't in RTO mode, so just report it.
  180.             // This is the state that we will be in when we have just begun successfully reporting
  181.             // queued stumbles that had previously failed or been queued up behind a failure.
  182.             this._reportStumbles(false);
  183.         }
  184.     },
  185.     
  186.     //
  187.     // _failsafeQuery
  188.     //
  189.     // We use this function for all stumble reporter database queries.  If we encounter errors performing database
  190.     // queries, then we switch to using prefs for the data storage.  We are doing this because we have seen error
  191.     // logs that indicate that a number of clients are not able to update the database.
  192.     //
  193.     _failsafeQuery: function(db)
  194.     {
  195.         try
  196.         {
  197.             return db.query();
  198.         }
  199.         catch(ex)
  200.         {
  201.             this._ds.setValue("@db_visitedurls_fail", true);
  202.             this._updateEnableDb();
  203.             this._parent.callWindow("su_log_error", "DB_VISITEDURLS_FAIL: " + ex);
  204.             throw ex;
  205.         }
  206.     },
  207.     
  208.     _updateEnableDb: function()
  209.     {
  210.         this._enabledb = this._ds._enabledb;
  211.         if(this._enabledb && 
  212.            (this._ds.getValue("@enable_visitedurls_failsafe") && this._ds.getValue("@db_visitedurls_fail")))
  213.         {
  214.             // If failsafe is enabled and we have had a visitedurls failure, them fall back to the prefs
  215.             // store.
  216.             this._enabledb = false;
  217.         }
  218.     },
  219.     
  220.     // Returns the current RTO timeout in milliseconds.
  221.     _getCurrentRTOTimeout: function()
  222.     {
  223.         var timeout = this._ds.lookup("stumblereportretrycount:timeout", this._retryCount);
  224.         if (timeout == null)
  225.             timeout = this._ds.lookup("stumblereportretrycount:timeout", "last");
  226.         // Note:  Value is stored in the ds in seconds.
  227.         return timeout * 1000;
  228.     },
  229.     
  230.     _getNextVisitedUrl: function()
  231.     {
  232.         if (this._enabledb)
  233.         {
  234.             var db = this._ds.getDatabase();
  235.             db.a("select * from stumble_visited_urls order by stumbletime asc");
  236.             var result = this._failsafeQuery(db);
  237.  
  238.             if (result.length == 0)
  239.                 return null;
  240.             
  241.             return result[0];
  242.         }
  243.         else
  244.         {
  245.             var result = this._ds.selectAllRows("stumble_visited_urls");
  246.             if (! result)
  247.                 return null;
  248.             
  249.             if (result.length == 0)
  250.                 return null;
  251.             
  252.             result.sort(function (a, b)
  253.                     { return parseInt(a.stumbletime) > parseInt(b.stumbletime); });
  254.             
  255.             return result[0];
  256.         }
  257.     },
  258.  
  259.     _reportStumbles: function(active)
  260.     {
  261.         // Get the next URL to report.
  262.         var url = this._getNextVisitedUrl();
  263.         
  264.         if (!url)
  265.         {
  266.             // nothing to do.
  267.             return;
  268.         }
  269.         // See if a report is already in progress.
  270.         
  271.         if (this._isReportInProgress())
  272.         {
  273.             // Someone is still trying to submit, so we won't.
  274.             return;
  275.         }
  276.         
  277.         var now = (new Date()).getTime();
  278.         
  279.         if (!active)
  280.         {
  281.             var throttleTime = this._ds.getValue("@stumble_report_throttle_ms");
  282.             // If we are passively reporting stumbles, then we want to throttle the reporting so we only 
  283.             // send at most 1 per minute.  This avoids hammering the server right after it has returned from a server-down
  284.             // situation.
  285.             if (now < (this._lastReportTimestamp + throttleTime))
  286.             {
  287.                 // Not enough time has elapsed, don't do it.
  288.                 return;
  289.             }
  290.         }
  291.         
  292.         // Update our state indicators so all toolbars know we are already submitting a report.
  293.         this._lastReportTimestamp = now;
  294.         this._reportContext = { url: url, active: active, quiet: true };
  295.         this._ds.setValue("$stumble_last_report", now);
  296.         this._ds.setValue("$stumble_report_started", now);
  297.         this._ds.flushPrefs();
  298.         
  299.         // Now submit the report.
  300.         var lastFailure = this._ds.getValue("$stumble_report_lastfail");
  301.         var params = "";
  302.         params = this._arp(params, "stumble_pid", url.publicid);
  303.         params = this._arp(params, "stumble_time", url.stumbletime);
  304.         params = this._arp(params, "retry", url.retrycount);
  305.         params = this._arp(params, "lastfailure", lastFailure);
  306.         params = this._arp(params, "clienttime", this._parent.callWindow("su_get_time_s"));
  307.         params = this._arp(params, "houroffset", Math.floor((new Date()).getTimezoneOffset() / 60));
  308.         if (url.referralid && (url.referralId != ''))
  309.             params = this._arp(params, "stumble_rid", url.referralid);
  310.         
  311.         var me = this;
  312.         this._parent.callWindow(
  313.                 "su_post_url_server_async",
  314.                 "report_stumble.php",
  315.                 params,
  316.                 15000,
  317.                 function() { me._onReportDone.apply(me, arguments); } ,
  318.                 this._reportContext );
  319.     },
  320.     
  321.     _onReportDone: function(res)
  322.     {
  323.         var context = {};
  324.         if (res.detail)
  325.             context = res.detail;
  326.         
  327.         // We are no longer in progress
  328.         this._reportContext = null;
  329.         this._ds.setValue("$stumble_report_started", 0);
  330.         this._ds.flushPrefs();
  331.         
  332.         // Check for timeout or failure
  333.         if (res.aborted || (res.status != 200))
  334.         {
  335.             if(res.aborted)
  336.             {
  337.                 if(!res.failureCode) 
  338.                     res.failureCode = this.FAIL_NORMAL_TIMEOUT;
  339.             }
  340.             else
  341.             {
  342.                 res.failureCode = res.status;
  343.             }
  344.             this._onReportFailed(res);
  345.             return;
  346.         }
  347.         
  348.         var strResponse = "";
  349.         if (typeof(res.responseText) != "undefined")
  350.             strResponse = res.responseText;
  351.         
  352.         if (this._parent._logCommunicationEnabled)
  353.             su_log("response report_stumble.php", strResponse);
  354.         
  355.         // Parse the response, on success it will have the SEENCONF command.
  356.         this._parent.callWindow(
  357.                 "su_process_commands",
  358.                 strResponse,
  359.                 context);
  360.         
  361.         if (context.confirmed)
  362.         {
  363.             // We got a confirmation, reset the retry count, remove the URL from
  364.             // the visited list, and report the next one.
  365.             this._retryCount = 0;
  366.             this._ds.setValue("$stumble_report_retrycount", this._retryCount);
  367.             this.deleteVisitedUrl(context.url);
  368.             this._ds.flushPrefs();
  369.             this._parent._setTimeout(
  370.                     function (this_, active) {
  371.                         this_._reportStumbles(active); },
  372.                     0,
  373.                     this,
  374.                     context.active);
  375.         }
  376.         else
  377.         {
  378.             // If we didn't get SEENCONF, then this this is a failure..
  379.             this._onReportFailed( { failureCode:this.FAIL_NO_SEENCONF } );
  380.         }
  381.     },
  382.     
  383.     _onReportFailed: function(result)
  384.     {
  385.         // This is a failure, move the retransmission state to 1 if it is the first
  386.         // failure.
  387.         if (this._retryCount == 0)
  388.         {
  389.             this._retryCount = 1;
  390.             this._ds.setValue("$stumble_report_retrycount", this._retryCount);
  391.         }
  392.  
  393.         
  394.         // Update any URLs currently in the database to reflect the new retry count
  395.         this._ds.setValue("$stumble_report_lastfail", result.failureCode);
  396.         this._updateVisitedUrlsRetryCount();
  397.         this._ds.flushPrefs();
  398.     },
  399.     
  400.     _updateVisitedUrlsRetryCount: function()
  401.     {
  402.         if (this._enabledb)
  403.         {
  404.             var db = this._ds.getDatabase();
  405.             db.a("UPDATE stumble_visited_urls SET retrycount=" + db.v(this._retryCount));
  406.             this._failsafeQuery(db);
  407.         }
  408.         else
  409.         {
  410.             var result = this._ds.selectAllRows("stumble_visited_urls");
  411.             
  412.             if (! result)
  413.                 return;
  414.             
  415.             var i;
  416.             
  417.             for (i = 0; i < result.length; i++)
  418.             {
  419.                 result[i].retrycount = this._retryCount;
  420.                 this._ds.updateRow(result[i]);
  421.             }
  422.         }
  423.     },
  424.  
  425.     _arp: function (paramStr, name, value, optGetFlag)
  426.     {
  427.         var delimiter;
  428.         if (optGetFlag && (paramStr.indexOf("&") == -1) &&
  429.                 (paramStr.indexOf("?") == -1))
  430.             delimiter = "?";
  431.         else
  432.             delimiter = "&";
  433.         
  434.         return paramStr + ((paramStr == "") ? "" : delimiter) + name + "=" + 
  435.                 encodeURIComponent(value);
  436.     }
  437. }
  438.  
  439.  
  440.